﻿using Apewer.Internals;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;

using static Apewer.TextUtility;

namespace Apewer.Source
{

    /// <summary>System.Data.DataTable 装箱查询。</summary>
    public sealed class Query : IQuery, IDisposable, IToJson
    {

        private bool _disposed = false;
        private bool _success = false;
        private string _message = null;
        private DataTable _table = null;
        private DataTable[] _tables = null;

        /// <summary>创建实例，默认状态为失败。</summary>
        public Query(bool success = false, string message = null)
        {
            _success = false;
            _message = message;
        }

        /// <summary>创建实例，Exception 为 NULL 时成功，非 NULL 时失败。</summary>
        public Query(Exception exception)
        {
            _success = exception == null;
            _message = RuntimeUtility.Message(exception);
        }

        /// <summary>创建实例，包装一个 DataTable 对象，数据表为 NULL 时失败，非 NULL 时成功。</summary>
        public Query(DataTable table)
        {
            _table = table;
            _success = table != null;
            _message = table == null ? "未获取有效的数据表。" : null;
        }

        /// <summary>创建实例，包装一个 DataTable 对象。</summary>
        public Query(DataTable table, bool success, string message = null)
        {
            _table = table;
            _success = success;
            _message = message;
        }

        /// <summary>创建实例，包装多个 DataTable 对象。</summary>
        public Query(DataTable[] tables, bool success = true, string message = null)
        {
            _tables = tables;
            _success = success;
            _message = message;
            if (tables != null && tables.Length > 0) _table = tables[0];
        }

        /// <summary>从 <see cref="Query"/> 到 <see cref="Boolean"/> 的隐式转换，判断 <see cref="Query"/> 执行成功且包含表。</summary>
        public static implicit operator bool(Query query) => query != null && query.Success && query.Table != null;

        /// <summary>从 <see cref="DataTable"/> 到 <see cref="Query"/> 的隐式转换。</summary>
        public static implicit operator Query(DataTable table) => new Query(table, table != null, null);

        /// <summary>从 <see cref="Query"/> 到 <see cref="DataTable"/> 的隐式转换。</summary>
        public static implicit operator DataTable(Query query) => query?.Table;

        #region Property

        /// <summary>语句执行成功。</summary>
        public bool Success { get => _success; }

        /// <summary>消息。</summary>
        public string Message { get => _message; }

        /// <summary>所有结果表。</summary>
        public DataTable[] Tables { get => _tables; }

        /// <summary>获取默认结果表。如果设置默认结果表，会丢失设置前的所有结果表。</summary>
        public DataTable Table { get => _table; }

        /// <summary>默认表中的数据总行数。</summary>
        public int Rows { get => _table == null ? 0 : _table.Rows.Count; }

        /// <summary>默认表中的数据总列数。</summary>
        public int Columns { get => _table == null ? 0 : _table.Columns.Count; }

        #endregion

        #region Value

        /// <summary>获取默认表中指定单元格的内容。</summary>
        /// <param name="rowIndex">行索引，从 0 开始。</param>
        /// <param name="columnIndex">列索引，从 0 开始。</param>
        public object Value(int rowIndex, int columnIndex) => Table.Value(rowIndex, columnIndex);

        /// <summary>获取默认表中指定单元的内容。</summary>
        /// <param name="rowIndex">行索引，从 0 开始。</param>
        /// <param name="columnName">列名称/字段名称，此名称不区分大小写。</param>
        public object Value(int rowIndex, string columnName) => Table.Value(rowIndex, columnName);

        #endregion

        #region Method

        /// <summary>释放系统资源。</summary>
        public void Dispose()
        {
            if (_disposed) return;
            if (_tables != null)
            {
                foreach (var table in _tables) RuntimeUtility.Dispose(table);
                _tables = null;
            }
            RuntimeUtility.Dispose(_table);
            _table = null;
            _tables = null;
            _disposed = true;
            // GC.SuppressFinalize(this);
        }

        #endregion

        #region 模型化、IToJson

        /// <summary>转换为 Json 对象。</summary>
        public Json ToJson()
        {
            var jsonObject = Json.NewObject();
            jsonObject.SetProperty("success", _success);
            jsonObject.SetProperty("message", _message);
            if (Table != null)
            {
                var columns = SourceUtility.ToJson(Table.Columns);
                jsonObject.SetProperty("columns", columns);

                var rows = SourceUtility.ToJson(Table.Rows);
                jsonObject.SetProperty("rows", rows);
            }
            return jsonObject;
        }

        /// <summary>转换为模型数组。</summary>
        public ObjectSet[] ToArray()
        {
            var oss = null as ObjectSet[];
            var table = _table;
            if (!_disposed && table != null)
            {
                var width = table.Columns.Count;
                var names = new string[width];
                for (var c = 0; c < width; c++)
                {
                    var name = table.Columns[c].ColumnName;
                    if (name.IsEmpty()) continue;
                    if (names.Contains(name)) continue;
                    names[c] = name;
                }

                var unnamed = 1;
                for (var c = 0; c < width; c++)
                {
                    if (names[c] != null) continue;
                    while (true)
                    {
                        var name = "unnamed_" + unnamed.ToString();
                        unnamed++;
                        if (names.Contains(name)) continue;
                        names[c] = name;
                        break;
                    }
                }

                var height = table.Rows.Count;
                oss = new ObjectSet[height];
                for (var r = 0; r < height; r++)
                {
                    var os = new ObjectSet();
                    var dict = os.Origin;
                    for (var c = 0; c < width; c++)
                    {
                        var v = Value(r, c);
                        dict.Add(names[c], v.IsNull() ? null : v);
                    }
                    oss[r] = os;
                }
            }
            return oss;
        }

        #endregion

    }

}
